import { info } from "./utils";


/**
 * 蓝牙设备类型
 */
export type BTDevice = UniApp.OnBluetoothDeviceFoundResult['devices'][0];

/**
 * 蓝牙服务
 */
export type BLEService = UniApp.GetBLEDeviceServicesSuccessData;

/**
 * 蓝牙特征值
 */
export type BLECharacteristic = UniApp.GetBLEDeviceCharacteristicsSuccessData;

/**
 * 蓝牙管理器
 */
export class BTMgr {
	private static instance: BTMgr | null = null;
	static get() {
		if (!BTMgr.instance) {
			BTMgr.instance = new BTMgr();
		}
		return BTMgr.instance;
	}
	
	isReady: boolean = false;
	isScaning: boolean = false;
	constructor() {}
	
	open(): Promise<boolean> {
		return new Promise((resolve, reject) => {
			uni.openBluetoothAdapter({
				success: _res => {
					this.isReady = true;
					resolve(true);
				},
				fail: err => {
					reject(err);
				}
			});
		});
	}
	
	close(): Promise<boolean> {
		return new Promise((resolve, reject) => {
			uni.closeBluetoothAdapter({
				success: _res => {
					this.isReady = false;
					resolve(true);
				},
				fail: err => {
					reject(err);
				}
			});
		});
	}
	
	state(): Promise<UniApp.GetBluetoothAdapterStateSuccess> {
		return new Promise((resolve, reject) => {
			uni.getBluetoothAdapterState({
				success: res => {
					const { discovering, available } = res;
					this.isReady = available;
					this.isScaning = discovering;
					resolve(res);
				},
				fail: err => {
					reject(err);
				}
			});
		});
	}
	
	onState(cb: (result: UniApp.OnBluetoothAdapterStateChangeResult) => void) {
		uni.onBluetoothAdapterStateChange(state => {
			const {discovering, available} = state;
			this.isReady = available;
			this.isScaning = discovering;
			cb(state);
		});
	}
	
	startScan(services: string[], allowDuplicatesKey: boolean, interval: number, powerLevel: 'low' | 'high' | 'medium') {
		return new Promise((resolve, reject) => {
			uni.startBluetoothDevicesDiscovery({
				services,
				allowDuplicatesKey,
				interval,
				powerLevel,
				success: res => {
					info(' [ startScan ] ', res);
					this.isScaning = true;
					resolve(res);
				},
				fail: err => {
					reject(err);
				}
			});
		});
	}
	
	stopScan(): Promise<boolean> {
		return new Promise((resolve, reject) => {
			uni.stopBluetoothDevicesDiscovery({
				success: _res => {
					info(' [ stopScan ] ', _res);
					this.isScaning = false;
					resolve(true);
				},
				fail: err => {
					reject(err);
				}
			});
		});
	}
	
	/**
	 * 监听新设备的接入
	 */
	onFound(cb: (result: UniApp.OnBluetoothDeviceFoundResult) => void) {
		uni.onBluetoothDeviceFound(res => {
			info(' [ onFound ] ', res);
			cb(res);
		});
	}
	
	/**
	 * 获取信号强度
	 */
	rssi(deviceId: string) {
		return new Promise((resolve, reject) => {
			uni.getBLEDeviceRSSI({
				deviceId,
				success: res => {
					info(' [ rssi ] ', res);
					resolve(res);
				},
				fail: err => {
					reject(err);
				}
			});
		});
	}
	
	

}


export interface OptionalDevice {
	deviceId: string;
	name?: string;
}

/**
 * BLE链接
 */
export class BLEMgr {
	private static instance: BLEMgr | null = null;
	static get() {
		if (!BLEMgr.instance) {
			BLEMgr.instance = new BLEMgr();
		}
		return BLEMgr.instance;
	}
	private constructor() {}
	
	connect(deviceId: string, timeout = 10_000): Promise<boolean> {
		return new Promise((resolve, reject) => {
			uni.createBLEConnection({
				deviceId,
				timeout,
				success: _res => {
					info(' [ connect ] ', _res);
					resolve(true);
				},
				fail: err => {
					reject(err);
				}
			});
		});
	}
	
	disconnet(deviceId: string): Promise<boolean> {
		return new Promise((resolve, reject) => {
			uni.closeBLEConnection({
				deviceId,
				success: _res => {
					info(' [ disconnet ] ', _res);
					resolve(true);
				},
				fail: err => {
					reject(err);
				}
			});
		});
	}
	
	// 获取服务
	services(deviceId: string): Promise<BLEService[]> {
		return new Promise((resolve, reject) => {
			uni.getBLEDeviceServices({
				deviceId,
				success: res => {
					info(' [ services ] ', res);
					resolve(res.services);
				},
				fail: err => {
					reject(err);
				}
			});
		});
	}
	
	// 获取特征值
	characteristics(deviceId: string, serviceId: string): Promise<BLECharacteristic[]> {
		return new Promise((resolve, reject) => {
			uni.getBLEDeviceCharacteristics({
				deviceId,
				serviceId,
				success: res => {
					info(' [ characteristics ] ', res.characteristics);
					resolve(res.characteristics);
				},
				fail: err => {
					reject(err);
				}
			});
		});
	}
	
	// 监听特征值的变化
	onValue(cb: (result: UniApp.OnBLECharacteristicValueChangeSuccess) => void) {
		uni.onBLECharacteristicValueChange(res => {
			info(' [ onValue ] ', res);
			cb(res);
		});
	}
	
	// 监听BLE的变化
	onState(cb: (result: UniApp.OnBLEConnectionStateChangeSuccess) => void) {
		uni.onBLEConnectionStateChange(res => {
			info(' [ onState ] ', res);
			cb(res);
		})
	}
	
	notify(deviceId: string, serviceId: string, characteristicId: string, state: boolean): Promise<boolean> {
		return new Promise((resolve, reject) => {
			uni.notifyBLECharacteristicValueChange({
				deviceId,
				serviceId,
				characteristicId,
				state,
				success: res => {
					info(' [ notify ] ', res);
					resolve(true);
				},
				fail: err => {
					reject(err);
				}
			});
		});
	}
	
	read(deviceId: string, serviceId: string, characteristicId: string): Promise<boolean> {
		return new Promise((resolve, reject) => {
			uni.readBLECharacteristicValue({
				deviceId,
				serviceId,
				characteristicId,
				success: res => {
					info(' [ read ] ', res);
					resolve(true);
				},
				fail: err => {
					reject(err);
				}
			});
		});
	}
	
	
	write(deviceId: string, serviceId: string, characteristicId: string, value: ArrayBuffer): Promise<boolean> {
		return new Promise((resolve, reject) => {
			let writeType:  'write' | 'writeNoResponse'  = 'write';
			if ((uni.getSystemInfoSync().platform === 'adnroid')) {
				writeType = 'writeNoResponse';
			}
			uni.writeBLECharacteristicValue({
				deviceId,
				serviceId,
				characteristicId,
				// WHY??? 为什么类型冲突
				value: value as unknown as any[],
				writeType,
				success: res => {
					info(' [ write ] ', res);
					resolve(true);
				},
				fail: err => {
					reject(err);
				}
			});
		});
	}
}


/**
 * 二进制转化为16进制字符串
 */ 
export function ab2hex (ab: ArrayBuffer) {
	const view = new Uint8Array(ab);
	
	const hexArr = [];
	for (let i = 0; i < view.length; i++) {
		const hexStr = view[i].toString(16).padStart(2, '0');
		hexArr.push(hexStr);
	}
	console.log('LOG ab2hex: ', hexArr, 'origin: ', ab);
	return hexArr.join('');
}


/**
 * 16禁止字符串转化为普通ASCII码
 */ 
export function hex2str(hexStr: string) {
	const isHexHead = hexStr.startsWith('0x');
	const rawHexStr = isHexHead ? hexStr.substring(2) : hexStr;
	if (rawHexStr.length % 2 !== 0) return '';
	const charArr = [];
	for (let i = 0; i < rawHexStr.length; i = i + 2) {
		const hexNum = parseInt(`${rawHexStr[i]}${rawHexStr[i + 1]}`, 10);
		const char = String.fromCodePoint(hexNum);
		charArr.push(char);
	}
	console.log('LOG hex2str: ', charArr, 'origin: ', hexStr);
	return charArr.join('');
}


/**
 *  BLE管理类
 */
export class BLEConn {
	private static bleMgr: BLEMgr = BLEMgr.get();
	/**
	 * 特征值监听
	 */
	private static valueChangeFnMap: Map<string, () => void> = new Map();
	/**
	 * 状态监听
	 */
	private static stateChangeFnMap: Map<string, () => void> = new Map();
	
	private static retryCount = 3;
	
	device: BTDevice | null = null;
	
	// 当前设备是否处于链接状态
	isConnecting: boolean = false;
	
	// 是否重连中
	isRetying: boolean = false;
	
	// 设备的服务
	services: BLEService[] = [];
	
	// 特征值
	characteristics: BLECharacteristic[] = [];
	
	
	
	constructor (device: BTDevice) {
		this.device = device;
	}
	
	// 开始链接
	async start(timeout = 10_000) {
		// 判断当前链接是否在连接中
		if (this.isConnecting) return;
		
		let currentRetryCount = 0;
		
		// 最多重试三次
		while(!this.isConnecting && currentRetryCount < BLEConn.retryCount) {
			try {
				// 这里可能throw error
				await BLEConn.bleMgr.connect(this.device.deviceId, timeout);
				this.isConnecting = true;
				currentRetryCount = 0;
				
				// 获取services
				const services = await BLEConn.bleMgr.services(this.device.deviceId);
				this.services = services;
			} catch (_e) {
				currentRetryCount += 1;
				console.log('LOG BLEConn start error: ', _e);
			}
		}
		
		return () => BLEConn.bleMgr.disconnet(this.device.deviceId);
	}
	
	async selectService(serviceId: string) {
		
	}
	
	read() {
		// return BLEConn.bleMgr.read(this.device.deviceId);
	}
	
	write() {
		
	}
}